package cn.jbolt.util;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.jbolt.ai.common.entity.QiniuConfig;
import cn.jbolt.util.cache.CacheUtil;
import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.util.Auth;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
public class QiniuUtil {

    @Autowired
    private QiniuConfig qiniuConfig;

    // 七牛云的Access Key
    private static String ACCESS_KEY;
    // 七牛云的Secret Key
    private static String SECRET_KEY;
    // 七牛云存储空间名称
    private static String BUCKET;
    //域名
    private static String DOMAIN;
    //区域
    private static String REGION;

    private static final Logger logger = LoggerFactory.getLogger(QiniuUtil.class);

    @PostConstruct
    public void init() {
        ACCESS_KEY = qiniuConfig.getAccessKey();
        SECRET_KEY = qiniuConfig.getSecretKey();
        BUCKET = qiniuConfig.getBucket();
        DOMAIN = qiniuConfig.getDomain();
        REGION = qiniuConfig.getRegion();
    }

    /**
     * 检查是否已配置七牛云。
     *
     * @return 如果配置存在返回true，否则返回false。
     */
    private static Result isQiniuConfigured() {
        boolean b = StrUtil.isNotBlank(ACCESS_KEY) &&
                StrUtil.isNotBlank(SECRET_KEY) &&
                StrUtil.isNotBlank(REGION) &&
                StrUtil.isNotBlank(DOMAIN);
        if (!b) {
            logger.error("配置文件中没有找到七牛的相关配置");
            return Result.error("请在配置文件中添加七牛云的相关配置。");
        }
        return Result.success();

    }

    /**
     * 生成指定bucket的token & region
     *
     * @param
     * @return
     */
    public static Result genUploadToken() {
        if (isQiniuConfigured().isError()) {
            return isQiniuConfigured();
        }
        Map<String, String> map = new HashMap<>();
        String qiniuToken = CacheUtil.getString(CacheUtil.KEY_QINIU_TOKEN);
        if (qiniuToken != null) { //token未过期
            map.put("token", qiniuToken);
            map.put("region", REGION);
            map.put("domain", DOMAIN);
            logger.info("从缓存中获取七牛的token,设置过期时间为55分钟-----{}:", qiniuToken);
        } else {//token 已过期
            // 创建Auth对象
            Auth auth = Auth.create(ACCESS_KEY, SECRET_KEY);
            // 生成上传凭证
            String str = auth.uploadToken(BUCKET);
            map.put("token", str);
            map.put("region", REGION);
            map.put("domain", DOMAIN);
            //这个地方的过期时间是55分钟,因为auth.uploadToken获取token的时候,源码里的token是60分钟失效
            CacheUtil.putString(CacheUtil.KEY_QINIU_TOKEN, str, 55, TimeUnit.MINUTES);
            logger.info("根据uploadToken接口获取七牛的token,设置过期时间为55分钟,已放入缓存-----{}:", str);
        }
        return Result.success(map);
    }

    /**
     * 通用的上传文件逻辑，支持字节数组和文件类型的文件上传
     *
     * @param fileData 要上传的文件数据，可以是字节数组或者File对象
     * @param fileName 上传到七牛云存储后的文件名（键）
     * @param filePath 文件路径
     * @return 包含上传结果信息的Result对象，如果上传成功则包含文件访问地址，否则包含错误信息
     * @throws QiniuException 如果上传过程出现七牛云相关异常
     */
    public static Result<String> uploadFile(Object fileData, String fileName,String filePath) throws QiniuException {
        //如果有filePath，则使用filePath作为文件名
        if (StrUtil.isNotBlank(filePath)) {
            fileName = filePath;
        }else{ //如果没有的话,那就按照默认
            fileName = "ai/"+DateUtil.today()+ "/"+  RandomUtil.randomString(6)+fileName;
        }
        if (isQiniuConfigured().isError()) {
            return isQiniuConfigured();
        }
        Configuration cfg = new Configuration(Region.region0());
        UploadManager uploadManager = new UploadManager(cfg);
        Auth auth = Auth.create(ACCESS_KEY, SECRET_KEY);
        String upToken = auth.uploadToken(BUCKET);

        Response response;
        if (fileData instanceof byte[]) {
            response = uploadManager.put((byte[]) fileData, fileName, upToken);
        } else if (fileData instanceof File) {
            response = uploadManager.put((File) fileData, fileName, upToken);
        } else if (fileData instanceof InputStream) {
            response = uploadManager.put((InputStream) fileData, fileName, upToken, null, null);
        } else {
            logger.error("不支持的文件数据类型: {}", fileData.getClass().getName());
            return Result.error("上传文件的数据类型不被支持，请检查传入参数。");
        }

        if (response.statusCode == 200) {
            String accessUrl = DOMAIN + "/" + fileName;
            logger.info("上传成功, 访问地址为: {}", accessUrl);
            return Result.success(accessUrl);
        }

        handleUploadError(response, fileName);
        String url = retryUpload(fileData, fileName, uploadManager, upToken);
        return Result.success(url);
    }

    /**
     * 处理上传失败时的错误，根据具体错误码等做相应处理（这里示例可根据实际进一步细化）
     *
     * @param response 上传操作返回的响应对象
     * @param fileName 上传的文件名，用于在日志中更清晰地记录错误关联信息
     */
    private static void handleUploadError(Response response, String fileName) {
        logger.error("上传文件 {} 失败, 错误信息: {}", fileName, response.toString());
    }

    /**
     * 重试上传文件逻辑，兼容字节数组和文件类型的文件数据
     *
     * @param fileData      要上传的文件数据，可以是字节数组或者File对象
     * @param key           上传到七牛云存储后的文件名（键）
     * @param uploadManager 上传管理器实例
     * @param upToken       上传凭证
     * @return 文件在七牛云的访问URL，如果重试后仍然失败返回null
     * @throws QiniuException 如果重试过程出现异常
     */
    private static String retryUpload(Object fileData, String key, UploadManager uploadManager, String upToken) {
        int retry = 0;
        while (retry < 3) {
            try {
                Response response;
                if (fileData instanceof byte[]) {
                    response = uploadManager.put((byte[]) fileData, key, upToken);
                } else if (fileData instanceof File) {
                    response = uploadManager.put((File) fileData, key, upToken);
                } else if (fileData instanceof InputStream) {
                    response = uploadManager.put((InputStream) fileData, key, upToken, null, null);
                } else {
                    logger.error("不支持的文件数据类型用于重试上传: {}", fileData.getClass().getName());
                    return null;
                }
                if (response.statusCode == 200) {
                    String url = DOMAIN + "/" + key;
                    logger.info("重试上传成功, 访问地址为: {}", url);
                    return url;
                }
                handleUploadError(response, key);
            } catch (QiniuException e) {
                handleRetryError(e, key);
            }finally {
                retry++;
            }
        }
        return null;
    }

    /**
     * 处理重试上传过程中的异常（这里示例可根据实际进一步细化）
     *
     * @param e   重试上传出现的七牛云异常
     * @param key 上传的文件名，用于在日志中更清晰地记录错误关联信息
     */
    private static void handleRetryError(QiniuException e, String key) {
        logger.error("重试上传文件 {} 出现异常，异常信息：{}", key, e.getMessage());
    }

}
